/*
 * Retainer.h
 *
 *  Created on: 12 Nov 2012
 *      Author: hopey
 */

#ifndef RETAINER_H_
#define RETAINER_H_
#include <memory>
#include <vector>
/**
 * A simple means of retaining smart references to objects that are created within some smaller scope without
 * needing to explicitly retain a reference.
 *
 * This is primarily motivated by the fact that I am creating GUI elements in various places, in create_components() methods
 * and similar, and I don't like having to declare a member field with a smart pointer to each box, button, label, etc that I add.
 * If this isn't done, then the widgets get destroyed as soon as the method ends, and they;re gone by the time the GUI loads.
 *
 * Instead, I can make the class whose components are being created extend Retainer<Widget>, and instaniate (within a method)
 * whatever widgets I'd like retained in memory for as long as the creating object exists, simply by calling retain() within the declaration.
 *
 * e.g.
 *
 * class SomeDialog : public Retainer<Widget> {
 * ...
 *
 * shared_ptr<Widget> createLayout() {
 * 		shared_ptr<VBox> box = retain(new VBox);
 * 		shared_ptr<Button> button = retain(new Button);
 * 		...
 * 		return box;
 * }
 * ...
 * }
 *
 * If these had been created as VBox box; or shared_ptr<Button> button(new Button); then they would be drstroyed as soon as the createLayout
 * scope closed. This way, they are retained for the life of the SomeDialog instance, on whose destruction they are also destroyed.
 *
 * Note that as with any other use of shared pointers, one should always pass the reference created by 'new' directly.
 *
 * Another option is to return the referenced object from retain, such that VBox box = retain(new VBox); however this prevents us from sharing the
 * resulting pointer with anything else, while the only advantage is slightly simpler syntax (. instead of -> and having to dereference it at some point.
 *
 * Note that one can declare that class can retain multiple types:
 *
 * class X : Retainer<Widget>, Retainer<string> {...}
 *
 * with the proviso that one can no longer use auto on declarations using the (now overloaded) retain() method.
 */
/*
 * I'd like to make it template <class T, class P=std::shared_ptr>, allowing use of and smart pointer,
 * but it don't work, as I'd need to define the retention list to be of type P<T> and the retain method return
 * P<E>, which I can't do. Returning P<T> would prevent you from doing anything on the resulting ptr, meaning
 * that it can't be called inline.
 *
 * I also can't templatise the vector to use other containers with push_back methods for similar reasons.
 */
template <class T>
class Retainer {

	/*
	 * We make these protected and allow access only through subclassing, which keeps
	 * exposed methods of classes that extend it smaller. We might instead choose to make them
	 * public, which would allow composition instead, and also to defer retention to other
	 * objects.
	 */
protected:
	Retainer(){}
	~Retainer(){}

	/*
	 * TODO: Shit, what about copy and assignment?
	 */
	/**
	 * Retains a shared smart pointer reference to the newly created object until this object is destroyed. Note that one should always
	 * directly pass the result of 'new' to this method, as messing around with the raw pointer could do weird stuff. A new shared pointer
	 * is created and returned which can be safely passed around.
	 *
	 * E can be any class assignable to T (any subclass).
	 */
	template <class E>
	std::shared_ptr<E> retain(E* newPtr) {
		std::shared_ptr<E> rPtr(newPtr);
		return retain(rPtr);
	}

	/**
	 * Retains a copy of the supplied shared pointer until this object is destroyed.
	 *
	 * E can be any class assignable to T (any subclass).
	 */
	template <class E>	std::shared_ptr<E> retain(std::shared_ptr<E> ptr) {
		retention_list.push_back(ptr); //This compiles? I figured I'd have to use a cast; E must be a subclass of T, but we don't know that here.
		return ptr;
	}
	/**
	 * Release all shared pointer references that have been added with retain. If no other associated shared references exist, the targets will
	 * be destroyed.
	 */
	void release_all() {
		retention_list.clear();
	}
private:
	//Would be nice if we could also parameterise the collection type
	std::vector<std::shared_ptr<T>> retention_list;

};


#endif /* RETAINER_H_ */
